package org.mineground;

import com.maxmind.geoip.LookupService;
import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.command.CommandExecutor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.mineground.commands.player.*;
import org.mineground.commands.player.admin.*;
import org.mineground.handlers.ConfigHandler;
import org.mineground.handlers.MySQLHandler;
import org.mineground.handlers.RandomMessageSender;
import org.mineground.handlers.irc.Handler;
import org.mineground.listener.BlockListener;
import org.mineground.listener.EntityListener;
import org.mineground.listener.PlayerListener;
import org.mineground.listener.ServerListener;
import org.mineground.player.MinegroundPlayer;
import org.mineground.player.ReactionTest;
import org.mineground.player.Supported;
import org.mineground.player.groupchat.GroupChatHandler;
import org.mineground.world.WarpLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @file Main.java (22.01.2012)
 * @author Daniel Koenen
 *
 */
public class Main extends JavaPlugin {
    private static final Logger ExceptionLogger = LoggerFactory.getLogger(Main.class);
    public static final Locale DEFAULT_LOCALE = new Locale("en", "US");
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    public static final String FILE_SEPERATOR = System.getProperty("line.separator");
    
    private static Main instance;
    
    private Listener playerListener;
    private Listener entityListener;
    private Listener blockListener;
    private Listener serverListener;
    
    private ConfigHandler configHandler;
    private MySQLHandler databaseHandler;
    private Handler ircHandler;
    private GroupChatHandler groupChatHandler;
    private ReactionTest reactionTestHandler;
    private LookupService geoIPService;
    private LookupService geoIPCityService;
    private RandomMessageSender randomMessageSender;
    
    private HashMap<Player, MinegroundPlayer> playerTable = new HashMap<Player, MinegroundPlayer>();
    private HashMap<Player, Player> teleportRequests = new HashMap<Player, Player>();
    
    public long lastTimeTick = 0;
    
    public Main() {
        instance = this;
    }

    @Override
    public void onEnable() {
        try {
            configHandler = new ConfigHandler(this.getConfig());
            databaseHandler = new MySQLHandler();
            ircHandler = new Handler();
            groupChatHandler = new GroupChatHandler();
            reactionTestHandler = new ReactionTest();
            randomMessageSender = new RandomMessageSender();
            geoIPService = new LookupService(new File(Main.getInstance().getDataFolder(), "GeoIP.dat"));

            loadWorlds();
            WarpLoader.load();
            Supported.initializeSupportedKeys();

            playerListener = new PlayerListener();
            entityListener = new EntityListener();
            blockListener = new BlockListener();
            serverListener = new ServerListener();

            registerEvents();
            registerCommands();
            registerIRCCommands();

            for (Player player : getServer().getOnlinePlayers()) {
                MinegroundPlayer playerInstance = new MinegroundPlayer(player);
                playerInstance.initializePlayer();
                playerInstance.loadPlayer();
                addPlayer(player, playerInstance);
            }
        }
        
        catch (Exception exception) {
            ExceptionLogger.error("Exception caught", exception);
        }
    }
    
    @Override
    public void onDisable() {
        for (Map.Entry<Player, MinegroundPlayer> entry : playerTable.entrySet()) {
            MinegroundPlayer playerInstance = entry.getValue();
            
            if (playerInstance != null && playerInstance.isRegistered() && playerInstance.isLoggedIn()) {
                entry.getValue().savePlayer();
            }
	}
        
        try {
            getDatabaseHandler().getConnection().close();
            playerTable.clear();
            getReactionTestHandler().killTimer();
            getIRCHandler().quitServer("Visit www.mineground.com");
            getGeoIPService().close();
        } 
        
        catch (Exception exception) {
            ExceptionLogger.error("Exception caught", exception);
        }
    }
    
    public MinegroundPlayer getPlayer(Player player) {
        return playerTable.get(player);
    }
    
    public void addPlayer(Player player, MinegroundPlayer instance) {
        playerTable.put(player, instance);
    }
    
    public void removePlayer(Player player) {
        playerTable.remove(player);
    }
    
    public boolean containsPlayer(Player player) {
        return playerTable.containsKey(player);
    }
    
    public void addTeleportRequest(Player player, Player recv) {
        teleportRequests.put(player, recv);
    }
    
    public void removeTeleportRequest(Player player) {
        teleportRequests.remove(player);
    }
    
    public boolean containsTeleport(Player player) {
        return playerTable.containsKey(player);
    }
    
    public Player getTeleportRequest(Player player) {
        return teleportRequests.get(player);
    }
    
    private void registerCommands() {
        Map<String, CommandExecutor> commands = new HashMap<String, CommandExecutor>();

	commands.put("rules", new Rules());
	commands.put("help", new Help());
	commands.put("commands", new Commands());
	commands.put("cmds", new Cmds());
	commands.put("pm", new PM());
	commands.put("r", new R());
	commands.put("pvp", new PvP());
	commands.put("ircpm", new IRCPM());
	commands.put("tp", new TP());
	commands.put("onlinetime", new OnlineTime());
	commands.put("warp", new Warp());
	commands.put("lastseen", new LastSeen());
	commands.put("online", new Online());
	commands.put("players", new Players());
        commands.put("groupchat", new GroupChat());
        commands.put("main", new MainWorld());
        commands.put("survival", new SurvivalWorld());
        commands.put("creative", new CreativeWorld());
        commands.put("classic" , new ClassicWorld());
        commands.put("spawn", new Spawn());
        commands.put("home", new Home());
        commands.put("login", new Login());
        commands.put("report", new Report());
        commands.put("fly", new Fly());

	commands.put("slay", new Slay());
	commands.put("time", new Time());
	commands.put("heal", new Heal());
	commands.put("god", new God());
	commands.put("forcepvp", new ForcePvP());
	commands.put("fetch", new Fetch());
	commands.put("weather", new Weather());
	commands.put("mtp", new MTP());
        commands.put("stp", new STP());
	commands.put("mode", new Mode());
        commands.put("resources", new Resources());
        commands.put("ban", new Ban());
        commands.put("kick", new Kick());
        commands.put("locate", new Locate());
        commands.put("mute", new Mute());
        commands.put("unmute", new Unmute());
        commands.put("i", new Give());
        commands.put("invsee", new InvSee());
        commands.put("clearinventory", new ClearInventory());

	for (Map.Entry<String, CommandExecutor> entry : commands.entrySet()) {
	    getCommand(entry.getKey()).setExecutor(entry.getValue());
        }
    }
    
    private void registerIRCCommands() {
        Map<String, org.mineground.handlers.irc.CommandExecutor> ircCommands = new HashMap<String, org.mineground.handlers.irc.CommandExecutor>();

	// Don't use the wrong CommandExecutor class for this.
	ircCommands.put("players", new org.mineground.commands.irc.Players());
	ircCommands.put("addnote", new org.mineground.commands.irc.AddNote());
	ircCommands.put("admin", new org.mineground.commands.irc.Admin());
	ircCommands.put("ban", new org.mineground.commands.irc.Ban());
	ircCommands.put("give", new org.mineground.commands.irc.Give());
	ircCommands.put("ip", new org.mineground.commands.irc.IP());
	ircCommands.put("kick", new org.mineground.commands.irc.Kick());
	ircCommands.put("msg", new org.mineground.commands.irc.MSG());
	ircCommands.put("pm", new org.mineground.commands.irc.PM());
	ircCommands.put("reload", new org.mineground.commands.irc.Reload());
	ircCommands.put("say", new org.mineground.commands.irc.Say());
	ircCommands.put("server", new org.mineground.commands.irc.Server());
	ircCommands.put("time", new org.mineground.commands.irc.Time());
	ircCommands.put("unban", new org.mineground.commands.irc.Unban());
	ircCommands.put("weather", new org.mineground.commands.irc.Weather());
	ircCommands.put("why", new org.mineground.commands.irc.Why());
	ircCommands.put("online", new org.mineground.commands.irc.Online());
	ircCommands.put("session", new org.mineground.commands.irc.Session());
	ircCommands.put("ses", new org.mineground.commands.irc.Session());
	ircCommands.put("nplayers", new org.mineground.commands.irc.NPlayers());
	ircCommands.put("ntotalplayers", new org.mineground.commands.irc.NTotalPlayers());
	ircCommands.put("commands", new org.mineground.commands.irc.Commands());
        ircCommands.put("resources", new org.mineground.commands.irc.Resources());
        ircCommands.put("raw", new org.mineground.commands.irc.Raw());
        ircCommands.put("save-all", new org.mineground.commands.irc.SaveAll());
        ircCommands.put("setlevel", new org.mineground.commands.irc.SetLevel());
        ircCommands.put("lastbanned", new org.mineground.commands.irc.LastBanned());
        ircCommands.put("isbanned", new org.mineground.commands.irc.IsBanned());
        ircCommands.put("mute", new org.mineground.commands.irc.Mute());
        ircCommands.put("unmute", new org.mineground.commands.irc.Unmute());
        ircCommands.put("banip", new org.mineground.commands.irc.BanIP());
        ircCommands.put("ipinfo", new org.mineground.commands.irc.IPInfo());
        ircCommands.put("unbanip", new org.mineground.commands.irc.UnbanIP());
        ircCommands.put("rangeexception", new org.mineground.commands.irc.RangeException());
        ircCommands.put("restart", new org.mineground.commands.irc.Restart());
        ircCommands.put("iplocation", new org.mineground.commands.irc.IPLocation());
        ircCommands.put("changeplayerpassword", new org.mineground.commands.irc.ChangePlayerPassword());
        ircCommands.put("plugin", new org.mineground.commands.irc.HandlePlugin());
        ircCommands.put("value", new org.mineground.commands.irc.Value());
        ircCommands.put("reloadmessages", new org.mineground.commands.irc.ReloadMessages());
        
        // Private commands
        ircCommands.put("changepassword", new org.mineground.commands.irc.query.ChangePassword());
        
	for (Map.Entry<String, org.mineground.handlers.irc.CommandExecutor> entry : ircCommands.entrySet()) {
	    getIRCHandler().commandHandler.addCommand(entry.getKey(), entry.getValue());
        }
    }

    private void registerEvents() {
        getServer().getPluginManager().registerEvents(getPlayerListener(), this);
        getServer().getPluginManager().registerEvents(getEntityListener(), this);
        getServer().getPluginManager().registerEvents(getBlockListener(), this);
        getServer().getPluginManager().registerEvents(getServerListener(), this);
    }
    
    private void loadWorlds() {
        WorldCreator worldCreator = new WorldCreator(getConfigHandler().classicWorldName);
        getServer().createWorld(worldCreator);
    }

    public Listener getPlayerListener() {
        return playerListener;
    }

    public Listener getEntityListener() {
        return entityListener;
    }

    public Listener getBlockListener() {
        return blockListener;
    }

    public Listener getServerListener() {
        return serverListener;
    }

    public ConfigHandler getConfigHandler() {
        return configHandler;
    }

    public MySQLHandler getDatabaseHandler() {
        return databaseHandler;
    }

    public Handler getIRCHandler() {
        return ircHandler;
    }

    public GroupChatHandler getGroupChatHandler() {
        return groupChatHandler;
    }

    public ReactionTest getReactionTestHandler() {
        return reactionTestHandler;
    }

    public LookupService getGeoIPService() {
        return geoIPService;
    }

    public LookupService getGeoIPCityService() {
        return geoIPCityService;
    }

    public RandomMessageSender getRandomMessageSender() {
        return randomMessageSender;
    }
    
    public static Main getInstance() {
        return instance;
    }
}
